package com.whfc.mach.manager.impl;

import com.alibaba.fastjson.JSON;
import com.whfc.common.constant.QueueConst;
import com.whfc.common.enums.DelFlag;
import com.whfc.common.enums.FenceType;
import com.whfc.common.enums.ObdWorkStatus;
import com.whfc.common.enums.WorkState;
import com.whfc.common.obd.ObdDataUtil;
import com.whfc.common.obd.ObdFrame;
import com.whfc.common.obd.ObdFrameItem;
import com.whfc.common.third.map.MapApi;
import com.whfc.common.third.map.MapLoc;
import com.whfc.common.util.*;
import com.whfc.influx.dao.AppDeviceObdLogDao;
import com.whfc.mach.dao.*;
import com.whfc.mach.dto.AppMachDayDTO;
import com.whfc.mach.dto.AppMachDaySegDTO;
import com.whfc.mach.dto.AppMachInfo;
import com.whfc.mach.dto.obd.*;
import com.whfc.mach.dto.warn.MachWarnCheckDTO;
import com.whfc.mach.entity.*;
import com.whfc.mach.enums.NetState;
import com.whfc.mach.manager.MachConfigManager;
import com.whfc.mach.manager.ObdDataMgr;
import com.whfc.mach.manager.util.MachSegmentUtil;
import com.whfc.mach.manager.util.MachUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @Description:
 * @author: xugcheng
 * @version: 1.0
 * @date: 2020/9/25 18:17
 */
@Service
public class ObdDataMgrImpl implements ObdDataMgr {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 获取OBD状态的前后时间区间(单位:秒)
     */
    private static final int OBD_STATE_TIME_IN_SECONDS = 120;

    @Autowired
    private AppMachDeviceMapper machDeviceMapper;

    @Autowired
    private AppMachDataMapper machDataMapper;

    @Autowired
    private AppMachDayMapper machDayMapper;

    @Autowired
    private AppMachDaySegMapper appMachDaySegMapper;

    @Autowired
    private AppDeviceObdDataMapper appDeviceObdDataMapper;

    @Autowired
    private AppDeviceObdLogDao appDeviceObdLogDao;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private MachConfigManager machConfigManager;

    @Autowired
    private AppMachMapper machMapper;

    @Autowired
    private AppMaintainMapper maintainMapper;

    @Autowired
    private MapApi mapApi;

    @Override
    public void addObdData(AppDeviceObdLog obdLog, List<ObdFrame> frameList) {
        Date serverTime = new Date();
        AppMachInfo machInfo = machDeviceMapper.selectAppMachInfoByDeviceId(obdLog.getDeviceId());
        Integer machId = null != machInfo ? machInfo.getMachId() : 0;
        obdLog.setMachId(machId);
        obdLog.setDelFlag(DelFlag.UNDELETE.getValue());
        obdLog.setNetState(NetState.ONLINE.getValue());
        // 预先解析数据
        this.parseObdLogData(obdLog, frameList);

        // OBD硬件,数据存储到influxDB
        appDeviceObdLogDao.insert(obdLog, frameList);

        // 更新实时工作状态
        this.updateDeviceWorkState(obdLog);

        // 更新硬件最新数据
        this.updateDeviceObdData(obdLog, frameList);

        // 数据处理
        if (!ObjectUtils.isEmpty(machId) && machId > 0) {

            // 更新设备实时数据
            this.updateMachData(machInfo, serverTime, obdLog);

            // 更新设备油耗
            if (hasOilFrame(frameList)) {
                this.updateMachOil(machInfo, obdLog.getTime());
            }

            // 更新设备工时
            if (!ObjectUtils.isEmpty(obdLog.getWorkState())) {
                this.updateMachDaySegment(machInfo, obdLog.getTime());
            }

            // 更新设备放线里程
            if (hasLengthFrame(frameList)) {
                this.updateMachTotalLength(machInfo, obdLog.getTime());
            }

            // 发布OBD设备报警事件
            this.pushAlarmCheckMsg(machInfo, obdLog);
        }
    }

    @Override
    public void addObdData(AppDeviceObdLog obdLog) {

        Date serverTime = new Date();
        AppMachInfo machInfo = machDeviceMapper.selectAppMachInfoByDeviceId(obdLog.getDeviceId());
        Integer machId = null != machInfo ? machInfo.getMachId() : 0;
        obdLog.setMachId(machId);
        obdLog.setNetState(NetState.ONLINE.getValue());
        obdLog.setDelFlag(DelFlag.UNDELETE.getValue());

        // 预先解析数据
        this.parseObdLogData(obdLog);

        // OBD硬件,数据存储到influxDB
        appDeviceObdLogDao.insert(obdLog);

        // 更新实时工作状态
        this.updateDeviceWorkState(obdLog);

        // 更新硬件最新数据
        this.updateDeviceObdData(obdLog);

        // 数据处理
        if (!ObjectUtils.isEmpty(machId) && machId > 0) {

            //更新设备实时数据
            this.updateMachData(machInfo, serverTime, obdLog);

            //更新设备油耗
            if (ObdDataUtil.ID_18FEE900 == obdLog.getFrameId() || ObdDataUtil.ID_152 == obdLog.getFrameId()) {
                this.updateMachOil(machInfo, obdLog.getTime());
            }

            //更新设备工时
            if (!ObjectUtils.isEmpty(obdLog.getWorkState())) {
                this.updateMachDaySegment(machInfo, obdLog.getTime());
            }

            //更新设备放线里程
            if (ObdDataUtil.ID_120 == obdLog.getFrameId() || ObdDataUtil.ID_121 == obdLog.getFrameId()) {
                this.updateMachTotalLength(machInfo, obdLog.getTime());
            }

            //发布OBD设备报警事件
            this.pushAlarmCheckMsg(machInfo, obdLog);
        }
    }

    @Override
    public void statMachDayData(AppMachInfo machInfo, Date date) {
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        Integer machId = machInfo.getMachId();
        // 获取一天油耗
        double oilWear = this.getOilWear(machId, startTime, endTime);

        // 获取总里程
        ObdDataDTO obdDataDTO = this.getTotalLength(machId, startTime, endTime);
        Double mileEfficExp = this.computeMileEfficExp(machId, date, obdDataDTO.getMile());

        AppMachDay machDay = new AppMachDay();
        machDay.setHoldDeptId(machInfo.getHoldDeptId());
        machDay.setMachId(machId);
        machDay.setDeviceId(machInfo.getDeviceId());
        machDay.setDate(date);
        machDay.setMileage(obdDataDTO.getMile());
        machDay.setMileage1(obdDataDTO.getMile1());
        machDay.setMileage2(obdDataDTO.getMile2());
        machDay.setOilWear(oilWear);
        machDay.setMileEfficExp(mileEfficExp);
        machDayMapper.insertUpdate(machDay);

    }

    @Override
    public void updateMachDaySegment(AppMachInfo machInfo, Date date) {
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        Integer machId = machInfo.getMachId();
        //原始数据
        List<AppDeviceObdLogDTO> logList = appDeviceObdLogDao.selectObdLogByMachId(machId, startTime, endTime);

        if (logList.size() > 0) {
            //计算工时
            List<AppMachDaySegDTO> segDTOList = this.computeSegment(logList);
            //保存工时分段
            String segments = JSONUtil.toString(segDTOList);
            AppMachDaySeg daySeg = new AppMachDaySeg();
            daySeg.setMachId(machId);
            daySeg.setDate(date);
            daySeg.setSegments(segments);
            appMachDaySegMapper.insertOrUpdate(daySeg);

            //更新总工时
            int workTimes = 0;
            int idleTimes = 0;
            for (AppMachDaySegDTO segDTO : segDTOList) {
                if (MachUtil.isRunState(segDTO.getWorkState())) {
                    workTimes += segDTO.getTimes();
                } else if (MachUtil.isIdleState(segDTO.getWorkState())) {
                    idleTimes += segDTO.getTimes();
                }
            }
            AppMachDay machDay = new AppMachDay();
            machDay.setHoldDeptId(machInfo.getHoldDeptId());
            machDay.setMachId(machInfo.getMachId());
            machDay.setDeviceId(machInfo.getDeviceId());
            machDay.setDate(date);
            machDay.setWorkTimes(workTimes);
            machDay.setIdleTimes(idleTimes);
            machDayMapper.insertUpdate(machDay);

            // 最新工时分段
            int size = segDTOList.size();
            if (size > 0) {
                AppMachDaySegDTO appMachDaySegDTO = segDTOList.get(size - 1);
                Integer times = appMachDaySegDTO.getTimes();
                machInfo.setTimes(times);
            }

        }
    }


    @Override
    public void statObdOil(Integer machId, Date date) {
        AppMachInfo machInfo = machDeviceMapper.selectAppMachInfoByMachId(machId);
        this.updateMachOil(machInfo, date);
    }

    @Override
    public void statObdSeg(Integer machId, Date date) {
        AppMachInfo machInfo = machDeviceMapper.selectAppMachInfoByMachId(machId);
        this.updateMachDaySegment(machInfo, date);
    }

    @Override
    public void statObdLength(Integer machId, Date date) {
        AppMachInfo machInfo = machDeviceMapper.selectAppMachInfoByMachId(machId);
        this.updateMachTotalLength(machInfo, date);
    }

    @Override
    public List<ObdFrame> getObdData(Integer deviceId) {
        List<AppDeviceObdData> dataList = appDeviceObdDataMapper.selectByDeviceId(deviceId);
        List<ObdFrame> list = new ArrayList<>(dataList.size());
        for (AppDeviceObdData data : dataList) {
            Long frameId = data.getFrameId();

            //frameData解析
            String frameDataDetail = data.getFrameDataDetail();
            ObdFrame frame = ObdDataUtil.parseObdFrame(frameId, data.getFrameData());
            if (!StringUtils.isEmpty(frameDataDetail)) {
                List<ObdFrameItem> frameItemList = JSONUtil.parseArray(frameDataDetail, ObdFrameItem.class);
                if (frameItemList != null && frameItemList.size() > 0) {
                    frame.setFrameDataItemList(frameItemList);
                }
            }
            list.add(frame);

            // 0x18ECFF00.frameData      = DM1/ DM2 BAM 当前/历史故障信息通告 (0x18ECFF00)
            // 0x18ECFF00.frameSubPacket = DM1/DM2 PACK 故障诊断码分包        (0x18EBFF00)
            if (ObdDataUtil.ID_18ECFF00 == frameId) {
                List<String> subPackets = JSONUtil.parseArray(data.getFrameSubPacket(), String.class);
                ObdFrame subPacketsFrame = ObdDataUtil.parseObdFrame18EBFF00(subPackets);
                list.add(subPacketsFrame);
            }
        }
        return list;
    }

    @Override
    public ObdParamDTO getObdParamData(Integer deviceId) {
        List<AppDeviceObdData> dataList = appDeviceObdDataMapper.selectByDeviceId(deviceId);
        // 存放obd参数
        List<ObdFrameItem> paramList = new ArrayList<>();
        // 存放obd的诊断数据
        List<ObdFrame> dtcDataList = new ArrayList<>();
        for (AppDeviceObdData data : dataList) {
            Long frameId = data.getFrameId();
            // frameData解析
            String frameDataDetail = data.getFrameDataDetail();
            if (!StringUtils.isEmpty(frameDataDetail)) {
                List<ObdFrameItem> frameItemList = JSONUtil.parseArray(frameDataDetail, ObdFrameItem.class);
                if (frameItemList != null && frameItemList.size() > 0) {
                    // 解析历史故障码
                    if (ObdDataUtil.ID_18FECA00 == frameId) {
                        ObdFrameItem obdFrameItem = frameItemList.get(frameItemList.size() - 1);
                        ObdFrame frame = new ObdFrame();
                        frame.setFrameId(frameId);
                        frame.setFrameIdHex(ByteUtil.toHexString(ObdDataUtil.ID_18FECA00));
                        frame.setFrameData(data.getFrameData());
                        frame.setFrameDataItemList(Arrays.asList(obdFrameItem));
                        dtcDataList.add(frame);
                        continue;
                    }
                    paramList.addAll(frameItemList);
                }
            }
            // 解析当前的故障码
            if (ObdDataUtil.ID_18ECFF00 == frameId) {
                List<String> subPackets = JSONUtil.parseArray(data.getFrameSubPacket(), String.class);
                ObdFrame subPacketsFrame = ObdDataUtil.parseObdFrame18EBFF00(subPackets);
                dtcDataList.add(subPacketsFrame);
            }
        }
        // 转换为map
        Map<String, Object> map = this.list2Map(paramList);
        String s = JSON.toJSONString(map);
        ObdFrameItemDTO obdFrameItemDTO = JSON.parseObject(s, ObdFrameItemDTO.class);

        ObdParamDTO data = new ObdParamDTO();
        data.setFrameItemDTO(obdFrameItemDTO);
        data.setFrameList(dtcDataList);
        return data;
    }

    /**
     * list转换为map
     *
     * @param paramList
     * @return
     */
    private Map<String, Object> list2Map(List<ObdFrameItem> paramList) {
        Map<String, Object> map = new HashMap<>(paramList.size());
        for (ObdFrameItem obdFrameItem : paramList) {
            map.put(obdFrameItem.getCode(), obdFrameItem.getValue());
        }
        return map;
    }

    @Override
    public AppDeviceObdDataDTO getObdData(Integer deviceId, Date time) {
        AppDeviceObdLogDTO logDTO = appDeviceObdLogDao.selectObdLogByDeviceId(deviceId, time);
        if (logDTO != null) {
            List<ObdFrame> frameList = new ArrayList<>(64);
            //通过反射解析数据
            ReflectionUtils.doWithFields(logDTO.getClass(), field -> {
                String frameIdHex = field.getName().replace("frameData", "");
                if (!StringUtils.isEmpty(frameIdHex)) {
                    field.setAccessible(true);
                    Object object = field.get(logDTO);
                    logger.debug("{} : {}", field.getName(), object);
                    if (!ObjectUtils.isEmpty(object)) {
                        String frameData = String.valueOf(object);
                        ObdFrame frame = ObdDataUtil.parseObdFrame(frameIdHex, frameData);
                        frameList.add(frame);
                    }
                }
            }, field -> field.getName().startsWith("frameData"));

            AppDeviceObdDataDTO dataDTO = new AppDeviceObdDataDTO();
            BeanUtils.copyProperties(logDTO, dataDTO);
            dataDTO.setFrameList(frameList);
            return dataDTO;
        }
        return null;
    }

    @Override
    public List<AppMachDayDTO> getMachDayDataList(Integer machId, Date startDate, Date endDate) {
        List<AppMachDayDTO> list = machDayMapper.selectMachDayDataList(machId, startDate, endDate);
        return list;
    }

    @Override
    public void addOfflineData(Integer machId, Date time) {
        AppMach appMach = machMapper.selectByPrimaryKey(machId);
        if (appMach == null) {
            return;
        }
        // OBD硬件,数据存储到influxDB
        AppDeviceObdLog obdLog = new AppDeviceObdLog();
        obdLog.setNetState(NetState.OFFLINE.getValue());
        obdLog.setDelFlag(DelFlag.UNDELETE.getValue());
        obdLog.setMachId(machId);
        obdLog.setDeviceId(appMach.getDeviceId());
        obdLog.setTime(time);
        appDeviceObdLogDao.insert(obdLog);

        // 更新工时分段
        AppMachInfo machInfo = new AppMachInfo();
        machInfo.setMachId(machId);
        machInfo.setDeviceId(appMach.getDeviceId());
        machInfo.setHoldDeptId(appMach.getHoldDeptId());
        this.updateMachDaySegment(machInfo, time);
    }

    @Override
    public Double getObdTotalWorkHour(Integer machId) {
        Double totalWorkHours = 0D;
        AppMachData appMachData = machDataMapper.selectByMachId(machId);
        if (appMachData != null) {
            totalWorkHours = appMachData.getTotalWorkHours();
        }
        return totalWorkHours;
    }

    @Override
    public Map<Integer, ObdMaintainDTO> getMachMaintainInfo(List<Integer> deptIds) {
        List<ObdMaintainDTO> obdMaintainDTOS = maintainMapper.selectMaintainList(deptIds, null, null, null);
        Map<Integer, ObdMaintainDTO> map = new HashMap<>();
        for (ObdMaintainDTO maintainDTO : obdMaintainDTOS) {
            map.putIfAbsent(maintainDTO.getMachId(), maintainDTO);
        }
        return map;
    }

    @Override
    public void amendObdDeviceData(AppDeviceObdLog data) {
        MapLoc location = mapApi.geocode(data.getLng(), data.getLat());
        if (location != null) {
            data.setLocation(location.getAddress());
            data.setProvince(location.getProvince());
            data.setCity(location.getCity());
        }
        // OBD硬件,数据存储到influxDB
        appDeviceObdLogDao.insert(data);
    }

    /**
     * 解析OBD数据
     *
     * @param obdLog
     */
    private AppDeviceObdLog parseObdLogData(AppDeviceObdLog obdLog) {
        Long frameId = obdLog.getFrameId();
        String frameData = obdLog.getFrameData();
        this.parseObdLogData(obdLog, frameId, frameData);
        return obdLog;
    }

    /**
     * 解析OBD数据(批量)
     *
     * @param obdLog
     * @param frameList
     * @return
     */
    private AppDeviceObdLog parseObdLogData(AppDeviceObdLog obdLog, List<ObdFrame> frameList) {
        if (frameList != null && !frameList.isEmpty()) {
            for (ObdFrame frame : frameList) {
                Long frameId = frame.getFrameId();
                String frameData = frame.getFrameData();
                frame.setFrameIdHex(Long.toHexString(frameId));
                this.parseObdLogData(obdLog, frameId, frameData);
            }
        }
        return obdLog;
    }

    /**
     * 解析OBD数据
     *
     * @param obdLog
     * @param frameId
     * @param frameData
     * @return
     */
    private AppDeviceObdLog parseObdLogData(AppDeviceObdLog obdLog, Long frameId, String frameData) {
        if (ObdDataUtil.ID_0CF00400 == frameId) {
            // 如果没有151数据帧则解析发动机转速
            if (!this.hasFrame151(obdLog.getDeviceId())) {
                List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
                double engineSpeed = ObdDataUtil.parseEngineSpeed(frameItemList);
                obdLog.setEngineSpeed(engineSpeed);
            }
        }
        if (ObdDataUtil.ID_18FEE900 == frameId) {
            // 如果没有152数据帧则解析总油耗
            if (!this.hasFrame152(obdLog.getDeviceId())) {
                List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
                double totalOilWear = ObdDataUtil.parseTotalFuelConsumption(frameItemList);
                obdLog.setTotalOilWear(totalOilWear);
            }
        }
        if (ObdDataUtil.ID_18FEE500 == frameId) {
            // 如果没有151数据帧则解析总工作时长
            if (!this.hasFrame151(obdLog.getDeviceId())) {
                List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
                double totalWorkHour = ObdDataUtil.parseTotalWorkHour(frameItemList);
                obdLog.setTotalWorkHours(totalWorkHour);
            }
        }
        //解析发动机转速
        if (ObdDataUtil.ID_151 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            double engineSpeed = ObdDataUtil.parseEngineSpeed(frameItemList);
            double totalWorkHours = ObdDataUtil.parseTotalWorkHour(frameItemList);
            obdLog.setEngineSpeed(engineSpeed);
            obdLog.setTotalWorkHours(totalWorkHours);
        }
        //解析1、2轮张力
        if (ObdDataUtil.ID_111 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            double tension1 = ObdDataUtil.parseTension1(frameItemList);
            obdLog.setTension1(tension1);
            double tension2 = ObdDataUtil.parseTension2(frameItemList);
            obdLog.setTension2(tension2);
        }
        //解析1轮转速
        if (ObdDataUtil.ID_113 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            double speed1 = ObdDataUtil.parseSpeed1(frameItemList);
            obdLog.setSpeed1(speed1);
            double force = ObdDataUtil.parseForce(frameItemList);
            obdLog.setForce(force);
        }
        //解析2轮/3轮转速
        if (ObdDataUtil.ID_114 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            double speed2 = ObdDataUtil.parseSpeed2(frameItemList);
            double speed3 = ObdDataUtil.parseSpeed3(frameItemList);
            obdLog.setSpeed2(speed2);
            obdLog.setSpeed3(speed3);
        }
        //解析4轮转速
        if (ObdDataUtil.ID_115 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            double speed4 = ObdDataUtil.parseSpeed2(frameItemList);
            obdLog.setSpeed4(speed4);
        }
        //解析总油耗
        if (ObdDataUtil.ID_152 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            double totalOilWear = ObdDataUtil.parseTotalFuelConsumption(frameItemList);
            obdLog.setTotalOilWear(totalOilWear);
        }
        //解析总里程1
        if (ObdDataUtil.ID_120 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            long totalLength1 = ObdDataUtil.parseTotalLength1(frameItemList);
            obdLog.setTotalLength1(totalLength1);
        }
        //解析总里程2
        if (ObdDataUtil.ID_121 == frameId) {
            List<ObdFrameItem> frameItemList = ObdDataUtil.parseObdData(frameId, frameData);
            long totalLength2 = ObdDataUtil.parseTotalLength2(frameItemList);
            obdLog.setTotalLength2(totalLength2);
        }
        // 设置预警
        if (ObdDataUtil.ID_101 == frameId) {
            obdLog.setErrFrameData(frameData);
        }

        return obdLog;
    }

    /**
     * 更新设备实时工作状态
     *
     * @param obdLog
     */
    private void updateDeviceWorkState(AppDeviceObdLog obdLog) {
        Integer machId = obdLog.getMachId();
        Date logTime = obdLog.getTime();
        Integer obdStatus = obdLog.getStatus();
        Date startTime = DateUtil.addSeconds(logTime, -OBD_STATE_TIME_IN_SECONDS);
        Date endTime = DateUtil.addSeconds(logTime, OBD_STATE_TIME_IN_SECONDS);
        ObdStateDTO stateDTO = appDeviceObdLogDao.selectObdState(machId, startTime, endTime);
        if (stateDTO != null) {
            Double engineSpeed = stateDTO.getEngineSpeed();
            List<Double> speedList = Arrays.asList(stateDTO.getSpeed1(), stateDTO.getSpeed2(), stateDTO.getSpeed3(), stateDTO.getSpeed4());
            WorkState workState = getObdWorkState(obdStatus, engineSpeed, speedList);
            if (workState != null) {
                obdLog.setWorkState(workState.getValue());
                appDeviceObdLogDao.insert(obdLog);
            }
        }
    }

    /**
     * 更新OBD硬件最新数据
     *
     * @param obdLog
     */
    private void updateDeviceObdData(AppDeviceObdLog obdLog) {

        Long frameId = obdLog.getFrameId();
        String frameData = obdLog.getFrameData();
        String frameDataDetail = obdLog.getFrameDataDetail();
        AppDeviceObdData obdData = this.parseDeviceObdData(obdLog, frameId, frameData, frameDataDetail);
        appDeviceObdDataMapper.insertOrUpdate(obdData);

    }

    /**
     * 更新OBD硬件最新数据(批量)
     *
     * @param obdLog
     * @param frameList
     */
    private void updateDeviceObdData(AppDeviceObdLog obdLog, List<ObdFrame> frameList) {
        if (frameList != null && !frameList.isEmpty()) {
            for (ObdFrame frame : frameList) {
                Long frameId = frame.getFrameId();
                String frameData = frame.getFrameData();
                String frameDataDetail = frame.getFrameDataDetail();
                AppDeviceObdData obdData = this.parseDeviceObdData(obdLog, frameId, frameData, frameDataDetail);
                appDeviceObdDataMapper.insertOrUpdate(obdData);
            }
        }
    }

    /**
     * 更新OBD硬件最新数据
     *
     * @param obdLog
     * @param frameId
     * @param frameData
     * @param frameDataDetail
     * @return
     */
    private AppDeviceObdData parseDeviceObdData(AppDeviceObdLog obdLog, Long frameId, String frameData, String frameDataDetail) {
        Integer deviceId = obdLog.getDeviceId();
        AppDeviceObdData record = new AppDeviceObdData();
        BeanUtils.copyProperties(obdLog, record);
        record.setDeviceId(deviceId);
        record.setFrameId(frameId);
        record.setFrameIdHex(Long.toHexString(frameId));
        record.setFrameData(frameData);
        record.setFrameDataDetail(frameDataDetail);

        //DM1/ DM2 BAM 当前/历史故障信息通告
        if (ObdDataUtil.ID_18ECFF00 == frameId) {
            int totalPackets = ObdDataUtil.parseTotalPackets(frameData);
            List<String> subPackets = new ArrayList<>(totalPackets);
            for (int i = 0; i < totalPackets; i++) {
                subPackets.add("");
            }
            record.setFrameSubPacket(JSONUtil.toString(subPackets));
        }
        //DM1/DM2 PACK 故障诊断码分包
        else if (ObdDataUtil.ID_18EBFF00 == frameId) {
            AppDeviceObdData subPacketsData = appDeviceObdDataMapper.selectByDeviceIdAndFrameId(deviceId, ObdDataUtil.ID_18ECFF00);
            //logger.info("subPacketsData:" + JSONUtil.toString(subPacketsData));
            if (subPacketsData != null) {
                List<String> subPackets = JSONUtil.parseArray(subPacketsData.getFrameSubPacket(), String.class);
                if (subPackets == null) {
                    int totalPackets = ObdDataUtil.parseTotalPackets(subPacketsData.getFrameData());
                    subPackets = new ArrayList<>(totalPackets);
                }
                int packetNo = ObdDataUtil.parseObdDmPacketNo(frameData);
                logger.debug("packetNo:{},subPackets:{}", packetNo, subPackets);
                //存储格式["01505000000000ff","02505000000000ff","03505000000000ff","04505000000000ff","05505000000000ff"]
                if (packetNo <= subPackets.size()) {
                    subPackets.set(packetNo - 1, frameData);
                }
                subPacketsData.setFrameSubPacket(JSONUtil.toString(subPackets));
            }
            record = subPacketsData;
        }
        return record;
    }

    /**
     * 更新设备实时数据
     *
     * @param machInfo
     * @param serverTime
     * @param obdLog
     */
    private void updateMachData(AppMachInfo machInfo, Date serverTime, AppDeviceObdLog obdLog) {
        Integer machId = machInfo.getMachId();
        Integer deptId = machInfo.getHoldDeptId();

        Integer deviceId = obdLog.getDeviceId();
        AppMachData machData = machDataMapper.selectByMachId(machId);
        if (machData == null) {
            machData = new AppMachData();
        }

        Integer oldWorkState = machData.getWorkState();
        Integer oldNetState = machData.getWorkState();
        Integer newNetState = NetState.ONLINE.getValue();

        //验证硬件时间
        Date logTime = obdLog.getTime();
        Date latestLogTime = machData.getTime();
        //历史数据(更新网络状态)
        if (MachUtil.isHistoryDataTime(logTime, latestLogTime)) {
            logger.info("硬件时间不正确,deviceId:{},machId:{},latestLogTime:{},logTime:{}", deviceId, machId, latestLogTime, logTime);
            machData.setMachId(machId);
            machData.setDeviceId(deviceId);
            machData.setNetState(NetState.ONLINE.getValue());
            machData.setUpdateTime(serverTime);
            machDataMapper.insertOrUpdate(machData);
        }
        //最新数据(更新工作状态和所有硬件数据)
        else {
            //工作状态
            Integer workStatus = obdLog.getWorkState();
            if (workStatus != null) {
                machData.setWorkState(obdLog.getWorkState());
                //工作状态-变化时间
                if (machData.getWorkStateStartTime() == null || !MachUtil.isSameState(oldWorkState, workStatus)) {
                    machData.setWorkStateStartTime(logTime);
                }
            }

            //网络状态-变化时间
            if (machData.getNetStateStartTime() == null || !newNetState.equals(oldNetState)) {
                machData.setNetStateStartTime(serverTime);
            }

            //经纬度
            String polygonWKT = machConfigManager.getPolygonWKT(deptId, machInfo.getMachTypeName());
            if (PositionUtil.contains(FenceType.POLYGON.value(), polygonWKT, null, obdLog.getLat(), obdLog.getLng())) {
                machData.setLng(obdLog.getLng());
                machData.setLat(obdLog.getLat());
                machData.setGpsTime(logTime);
                machData.setLngWgs84(obdLog.getLngWgs84());
                machData.setLatWgs84(obdLog.getLatWgs84());
                machData.setLocation(obdLog.getLocation());
                machData.setProvince(obdLog.getProvince());
                machData.setCity(obdLog.getCity());
            }
            machData.setTime(logTime);
            machData.setMachId(machId);
            machData.setDeviceId(deviceId);
            machData.setNetState(NetState.ONLINE.getValue());
            machData.setUpdateTime(serverTime);
            machData.setBatteryPower(obdLog.getBatteryPower());
            machData.setBatteryState(obdLog.getBatteryState());
            machData.setTotalLength1(obdLog.getTotalLength1());
            machData.setTotalLength2(obdLog.getTotalLength2());
            machData.setTotalOilWear(obdLog.getTotalOilWear());
            machData.setTotalWorkHours(obdLog.getTotalWorkHours());
            machDataMapper.insertOrUpdate(machData);
        }
    }

    /**
     * 更新设备每日油耗
     *
     * @param machInfo
     * @param date
     */
    private void updateMachOil(AppMachInfo machInfo, Date date) {
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        double oilWear = this.getOilWear(machInfo.getMachId(), startTime, endTime);
        if (oilWear > 0) {
            AppMachDay machDay = new AppMachDay();
            machDay.setHoldDeptId(machInfo.getHoldDeptId());
            machDay.setMachId(machInfo.getMachId());
            machDay.setDeviceId(machInfo.getDeviceId());
            machDay.setDate(date);
            machDay.setOilWear(oilWear);
            machDayMapper.insertUpdate(machDay);
        }
    }


    /**
     * 更新设备放线总里程
     *
     * @param machInfo
     * @param date
     */
    private void updateMachTotalLength(AppMachInfo machInfo, Date date) {
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        Integer machId = machInfo.getMachId();
        ObdDataDTO obdDataDTO = this.getTotalLength(machId, startTime, endTime);
        logger.debug("放线总里程,machId:{},obdDataDTO:{}", machId, obdDataDTO.toString());

        //更新里程
        Double mileEfficExp = this.computeMileEfficExp(machId, DateUtil.getDate(date), obdDataDTO.getMile());
        AppMachDay machDay = new AppMachDay();
        machDay.setMileEfficExp(mileEfficExp);
        machDay.setHoldDeptId(machInfo.getHoldDeptId());
        machDay.setMachId(machId);
        machDay.setDate(date);
        machDay.setDeviceId(machInfo.getDeviceId());
        machDay.setMileage(obdDataDTO.getMile());
        machDay.setMileage1(obdDataDTO.getMile1());
        machDay.setMileage2(obdDataDTO.getMile2());
        machDayMapper.insertUpdate(machDay);
    }


    /**
     * 计算设备的有效工效
     *
     * @param machId
     * @param date
     * @return
     */
    private Double computeMileEfficExp(Integer machId, Date date, Long mile) {
        Double mileEfficExp = 0D;
        AppMachDayDTO appMachDayDTO = machDayMapper.selectByMachIdAndDate(machId, date);
        if (appMachDayDTO != null && appMachDayDTO.getTimes() != null && appMachDayDTO.getTimes() != 0) {
            mileEfficExp = MathUtil.round(1.0 * mile / appMachDayDTO.getTimes(), 2);
        }
        return mileEfficExp;
    }

    /**
     * 计算工时
     *
     * @param logList
     * @return
     */
    private List<AppMachDaySegDTO> computeSegment(List<AppDeviceObdLogDTO> logList) {
        List<AppMachDaySegDTO> list = new ArrayList<>();
        if (logList.size() > 0) {
            // 硬件工时分段
            List<AppMachDaySegDTO> segList = MachSegmentUtil.separateMachDaySegment(logList);
            // 获取设备网络状态分段
            List<AppMachDaySegDTO> networkSegment = MachSegmentUtil.separateNetworkSegment(logList);
            // 综合工作状态和网络状态分段
            list = MachSegmentUtil.separateWorkStateAndNetState(segList, networkSegment);
        }
        // 过滤掉短时间的离线数据
        MachSegmentUtil.amendOfflineDataSegment(list);
        return list;
    }

    /**
     * 获取OBD设备工作状态
     *
     * @param obdStatus   电源状态
     * @param engineSpeed 发动机转速
     * @param speedList   轮速/牵引速度
     * @return
     */
    private WorkState getObdWorkState(Integer obdStatus, Double engineSpeed, List<Double> speedList) {
        // 发动机转速没有时判定为停机
        if (engineSpeed == null) {
            return WorkState.STOP;
        }

        //判断轮速
        boolean hasSpeed = false;
        if (speedList != null && speedList.size() > 0) {
            for (Double speed : speedList) {
                if (speed != null && speed > 0.1) {
                    hasSpeed = true;
                    continue;
                }
            }
        }

        //断电 or 关机
        if (ObdWorkStatus.POWER_OFF.getValue().equals(obdStatus) || ObdWorkStatus.STOP.getValue().equals(obdStatus)) {
            return WorkState.STOP;
        }
        //工作
        else {
            if (engineSpeed != null) {
                //转速<300
                if (engineSpeed < 300) {
                    return WorkState.STOP;
                }
                //转速>=300
                else {
                    //有轮速
                    if (hasSpeed) {
                        return WorkState.RUN;
                    }
                    //无轮速
                    else {
                        return WorkState.IDLE;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取一段时间的放线总里程
     *
     * @param machId
     * @param startTime
     * @param endTime
     * @return
     */
    private ObdDataDTO getTotalLength(Integer machId, Date startTime, Date endTime) {
        ObdLengthDTO data1 = appDeviceObdLogDao.selectObdLength(machId, startTime, endTime, "totalLength1");
        ObdLengthDTO data2 = appDeviceObdLogDao.selectObdLength(machId, startTime, endTime, "totalLength2");
        long total1 = 0;
        long total2 = 0;
        if (data1 != null) {
            Long first = data1.getFirstTotalLength();
            Long last = data1.getLastTotalLength();
            if (first != null && last != null && last > first) {
                total1 = (last - first);
            }
        }
        if (data2 != null) {
            Long first = data2.getFirstTotalLength();
            Long last = data2.getLastTotalLength();
            if (first != null && last != null && last > first) {
                total2 = (last - first);
            }
        }
        long totalLength = total1 + total2;

        ObdDataDTO data = new ObdDataDTO();
        data.setMile(totalLength);
        data.setMile1(total1);
        data.setMile2(total2);
        return data;
    }

    /**
     * 获取一段时间的油耗
     *
     * @param machId
     * @param startTime
     * @param endTime
     * @return
     */
    private double getOilWear(Integer machId, Date startTime, Date endTime) {
        double oilWear = 0;
        ObdOilDTO oilDTO = appDeviceObdLogDao.selectObdOil(machId, startTime, endTime);
        if (oilDTO != null && !ObjectUtils.isEmpty(oilDTO.getFirstTotalOilWear()) && !ObjectUtils.isEmpty(oilDTO.getLastTotalOilWear())) {
            double firstOilWear = oilDTO.getFirstTotalOilWear();
            double lastOilWear = oilDTO.getLastTotalOilWear();
            oilWear = lastOilWear > firstOilWear ? lastOilWear - firstOilWear : 0;
        }
        return oilWear;
    }

    /**
     * 有燃油消耗数据帧
     *
     * @param frameList
     * @return
     */
    private boolean hasOilFrame(List<ObdFrame> frameList) {
        if (frameList != null && !frameList.isEmpty()) {
            for (ObdFrame frame : frameList) {
                if (ObdDataUtil.ID_18FEE900 == frame.getFrameId() || ObdDataUtil.ID_152 == frame.getFrameId()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 有放线里程数据帧
     *
     * @param frameList
     * @return
     */
    private boolean hasLengthFrame(List<ObdFrame> frameList) {
        if (frameList != null && !frameList.isEmpty()) {
            for (ObdFrame frame : frameList) {
                if (ObdDataUtil.ID_120 == frame.getFrameId() || ObdDataUtil.ID_121 == frame.getFrameId()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 硬件是否上传过151数据帧
     *
     * @param deviceId
     * @return
     */
    private boolean hasFrame151(Integer deviceId) {
        AppDeviceObdData appDeviceObdData = appDeviceObdDataMapper.selectByDeviceIdAndFrameId(deviceId, ObdDataUtil.ID_151);
        return appDeviceObdData != null;
    }

    /**
     * 硬件是否上传过152数据帧
     *
     * @param deviceId
     * @return
     */
    private boolean hasFrame152(Integer deviceId) {
        AppDeviceObdData appDeviceObdData = appDeviceObdDataMapper.selectByDeviceIdAndFrameId(deviceId, ObdDataUtil.ID_152);
        return appDeviceObdData != null;
    }

    /**
     * 推送报警检测消息到队列
     */
    private void pushAlarmCheckMsg(AppMachInfo machInfo, AppDeviceObdLog obdLog) {
        MachWarnCheckDTO data = new MachWarnCheckDTO();
        Integer deptId = machInfo.getHoldDeptId();
        data.setFrameData(obdLog.getErrFrameData());
        data.setTriggerTime(obdLog.getTime());
        data.setLat(obdLog.getLat());
        data.setLng(obdLog.getLng());
        data.setWorkState(obdLog.getWorkState());
        data.setTimes(machInfo.getTimes());
        data.setMachId(machInfo.getMachId());
        data.setDeptId(deptId);
        data.setMachCode(machInfo.getMachCode());
        data.setMachType(machInfo.getMachType());
        data.setMachTypeName(machInfo.getMachTypeName());
        data.setMachModelName(machInfo.getMachModelName());
        data.setForce(obdLog.getForce());
        amqpTemplate.convertAndSend(QueueConst.ALARM_CHECK_DATA, JSONUtil.toString(data));
    }

}
